/////////////////////////////////////////////////////
// File Name: hash_lut.v
// Author: zeping fan
// mail:   zpfan007@163.com
// Created Time: 2023年06月15日 星期四 15时20分55秒
/////////////////////////////////////////////////////
module hash_lut#(
    parameter   TTL_NUM = 10'd300
)
(
input               clk,
input               rst_n,

//port se signals
input               se_source,      //为1时，表示学习过程；为0时，表示查找过程
input   [47:0]      se_mac,         
input   [15:0]      se_portmap,
input   [9:0]       se_hash,
input               se_req,
output  reg         se_ack,
output  reg         se_nak,
output  reg [15:0]  se_result,
input               aging_req,
output  reg         aging_ack
);

localparam  IDLE    =   8'b00000001;
localparam  LEARN   =   8'b00000010;
localparam  MATCH   =   8'b00000100;
localparam  MISMATCH=   8'b00001000;
localparam  SEARCH  =   8'b00010000;
localparam  AGING   =   8'b00100000;
localparam  DONE    =   8'b01000000;
localparam  INIT    =   8'b10000000;


//状态机变量
reg     [7:0]   cur_state;
reg     [7:0]   nxt_state;

//输入变量寄存
reg     [47:0]  se_mac_reg;
reg     [15:0]  se_portmap_reg;
reg     [9:0]   se_hash_reg;


reg             cfg_clr;    //表项清除控制寄存器，用于初始化阶段
wire            clr_done;   //
//ram0 as hash bucket0
reg             ram0_en;
reg             ram0_wr;    //1为写，0为读
reg     [9:0]   ram0_addr;
reg     [79:0]  ram0_din;
reg     [79:0]  ram0_dout;

//ram1 as hash bucket1
reg             ram1_en;
reg             ram1_wr;    //1为写，0为读
reg     [9:0]   ram1_addr;
reg     [79:0]  ram1_din;
reg     [79:0]  ram1_dout;

wire    [9:0]   ttl_ctx;    
//aging
reg     [9:0]   aging_addr;


//item_ttl表示两个表项的生存时间
wire    [9:0]   item0_ttl;
wire    [9:0]   item1_ttl;

//hit表示当前se_mac与哈希桶中的MAC是否相同，1表示相同，0表示不同
wire            hit0;
wire            hit1;


assign  item0_ttl[9:0] = ram0_dout[73:64];
assign  item1_ttl[9:0] = ram1_dout[73:64]; 
assign  hit1 = se_mac_reg[47:0]==ram1_dout[63:16] & ram1_dout[79];
assign  hit0 = se_mac_reg[47:0]==ram0_dout[63:16] & ram0_dout[79];

assign  ttl_ctx[9:0]   = TTL_NUM;

//========================三段式状态机========================
always @(posedge clk or negedge rst_n)begin
    if(!rst_n)
        cur_state[7:0] <= IDLE;
    else
        cur_state[7:0] <= nxt_state[7:0];
end

always @(*)begin
    case(cur_state[7:0])
        IDLE:       nxt_state[7:0] = cfg_clr? INIT : (se_source & se_req & ram0_en)? LEARN : (!se_source & se_req & ram0_en)? SEARCH : (aging_req & ram0_en)? AGING : IDLE;
        LEARN:      nxt_state[7:0] = (hit0 | hit1)? MATCH : MISMATCH;
        MATCH:      nxt_state[7:0] = DONE;
        MISMATCH:   nxt_state[7:0] = DONE;
        SEARCH:     nxt_state[7:0] = DONE;
        AGING:      nxt_state[7:0] = DONE;
        DONE:       nxt_state[7:0] = IDLE;
        INIT:       nxt_state[7:0] = clr_done? IDLE : INIT;
        default:    nxt_state[7:0] = IDLE;
    endcase
end

//=========================输入变量缓存========================
always @(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        se_mac_reg[47:0] <= 48'b0;
        se_hash_reg[9:0] <= 10'b0;
        se_portmap_reg[15:0] <= 16'b0;
    end
    else if(se_req)begin
        se_mac_reg[47:0] <= se_mac[47:0];
        se_hash_reg[9:0] <= se_hash[9:0];
        se_portmap_reg[15:0] <= se_portmap[15:0];
    end
end

//=========================clr控制器===========================
always @(posedge clk or negedge rst_n)begin
    if(!rst_n)
        cfg_clr <= 1'b1;
    else if(cur_state[7:0]==INIT)
        cfg_clr <= 1'b0;
end

assign clr_done = cur_state[7] && ram0_addr[9:0]== 10'h3ff;


//==========================ram控制============================
always @(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        ram0_en <= 1'b0;
        ram0_wr <= 1'b0;
        ram0_addr[9:0] <= 10'b0;
        ram0_din[79:0] <= 80'b0;

        ram1_en <= 1'b0;
        ram1_wr <= 1'b0;
        ram1_addr[9:0] <= 10'b0;
        ram1_din[79:0] <= 80'b0;
    end
    else begin
        case(cur_state[7:0])
            IDLE:begin
                if(se_req | aging_req)begin
                    ram0_en <= 1'b1;
                    ram0_wr <= 1'b0;
                    ram0_addr[9:0] <= se_req? se_hash[9:0] : aging_addr[9:0];

                    ram1_en <= 1'b1;
                    ram1_wr <= 1'b0;
                    ram1_addr[9:0] <= se_req? se_hash[9:0] : aging_addr[9:0];
                end
                else if(cfg_clr)begin
                    ram0_en <= 1'b1;
                    ram0_wr <= 1'b1;
                    ram0_addr[9:0] <= 10'b0;
                    ram0_din[79:0] <= 80'b0;
                    
                    ram1_en <= 1'b1;
                    ram1_wr <= 1'b1;
                    ram1_addr[9:0] <= 10'b0;
                    ram1_din[79:0] <= 80'b0;
                end
            end        

            MISMATCH:begin
                case({ram1_dout[79],ram0_dout[79]}) //判断表项是否有效
                    2'b11:begin
                        ram0_en <= 1'b0;
                        ram1_en <= 1'b0;
                    end
                    2'b00,2'b10:begin
                        ram0_en <= 1'b1;
                        ram0_wr <= 1'b1;
                        ram0_din[79:0] <= {1'b1,5'b0,ttl_ctx[9:0],se_mac_reg[47:0],se_portmap_reg[15:0]};
                        ram0_addr[9:0] <= se_hash_reg[9:0];

                        ram1_en <= 1'b0;
                    end
                    2'b01:begin
                        ram0_en <= 1'b0;                     

                        ram1_en <= 1'b1;
                        ram1_wr <= 1'b1;
                        ram1_din[79:0] <= {1'b1,5'b0,ttl_ctx[9:0],se_mac_reg[47:0],se_portmap_reg[15:0]};
                        ram1_addr[9:0] <= se_hash_reg[9:0];
                    end
                endcase
            end

            MATCH:begin
                case({hit1,hit0})
                    2'b01:begin
                        ram0_en <= 1'b1;
                        ram0_wr <= 1'b1;
                        ram0_addr[9:0] <= se_hash_reg[9:0];
                        ram0_din[79:0] <= {1'b1,5'b0,ttl_ctx[9:0],se_mac_reg[47:0],se_portmap_reg[15:0]};

                        ram1_en <= 1'b0;
                    end
                    2'b10:begin
                        ram0_en <= 1'b0;

                        ram1_en <= 1'b1;
                        ram1_wr <= 1'b1;
                        ram1_addr[9:0] <= se_hash_reg[9:0];
                        ram1_din[79:0] <= {1'b1,5'b0,ttl_ctx[9:0],se_mac_reg[47:0],se_portmap_reg[15:0]};
                    end
                endcase
            end
            
            AGING:begin
                ram0_wr <= 1'b1;
                ram0_din[79:0] <= (item0_ttl[9:0]>0)? {1'b1,5'b0,item0_ttl[9:0]-1'b1,ram0_dout[63:0]} : 80'b0;;

                ram1_wr <= 1'b1;
                ram1_din[79:0] <= (item1_ttl[9:0]>0)? {1'b1,5'b0,item1_ttl[9:0]-1'b1,ram1_dout[63:0]} : 80'b0;
            end

            DONE:begin
                ram0_en <= 1'b0;
                ram0_wr <= 1'b0;
                ram0_addr[9:0] <= 10'b0;
                ram0_din[79:0] <= 80'b0;

                ram1_en <= 1'b0;
                ram1_wr <= 1'b0;
                ram1_addr[9:0] <= 10'b0;
                ram1_din[79:0] <= 80'b0;
            end

            INIT:begin
                ram0_en <= (!clr_done)? 1'b1 : 1'b0;
                ram0_wr <= (!clr_done)? 1'b1 : 1'b0;
                ram0_addr[9:0] <= (!clr_done)? ram0_addr[9:0]+1'b1 : 10'b0;
                
                ram1_en <= (!clr_done)? 1'b1 : 1'b0;
                ram1_wr <= (!clr_done)? 1'b1 : 1'b0;
                ram1_addr[9:0] <= (!clr_done)? ram1_addr[9:0]+1'b1 : 10'b0;
            end
        endcase
    end
end


//============================应答控制======================================
always @(posedge clk or negedge rst_n)begin
    if(!rst_n)
        se_ack <= 1'b0;
    else begin
        case(cur_state[7:0])
            IDLE:       se_ack <= 1'b0;
            MISMATCH:   se_ack <= ({ram1_dout[79],ram0_dout[79]}!=2'b11)? 1'b1 : 1'b0;        
            MATCH:      se_ack <= 1'b1;
            SEARCH:     se_ack <= (hit1 | hit0)? 1'b1 : 1'b0;
            default:    se_ack <= 1'b0;
        endcase
    end
end

always @(posedge clk or negedge rst_n)begin
    if(!rst_n)
        se_nak <= 1'b0;
    else begin
        case(cur_state[7:0])
            IDLE:       se_nak <= 1'b0;
            MISMATCH:   se_nak <= (ram1_dout[79] & ram0_dout[79])? 1'b1 : 1'b0;
            SEARCH:     se_nak <= ({hit1,hit0}==2'b00)? 1'b1 : 1'b0;
            default:    se_nak <= 1'b0;
        endcase 
    end
end

//============================输出端口映射位图=============================
always @(posedge clk or negedge rst_n)begin
    if(!rst_n)
        se_result[15:0] <= 16'b0;
    else if(cur_state[7:0]==SEARCH)begin
        case({hit1,hit0})
            2'b01:  se_result[15:0] <= ram0_dout[15:0];
            2'b10:  se_result[15:0] <= ram1_dout[15:0];
            default:se_result[15:0] <= 16'b0;
        endcase
    end
end

//==============================老化操作===================================
always @(posedge clk or negedge rst_n)begin
    if(!rst_n)
        aging_addr[9:0] <= 10'b0;
    else if(cur_state[7:0]==DONE & aging_req)
        aging_addr[9:0] <= (aging_addr[9:0]<10'h3ff)? aging_addr[9:0]+1'b1 : 10'b0;
end

always @(posedge clk or negedge rst_n)begin
    if(!rst_n)
        aging_ack <= 1'b0;
    else if(cur_state[7:0]==AGING && aging_addr==10'h3ff)
        aging_ack <= 1'b1;
    else 
        aging_ack <= 1'b0;
end




hash_bucket x_hash_bucket0(
    .clk(clk),
    .rst_n(rst_n),
    .en(ram0_en),
    .wr(ram0_wr),
    .addr(ram0_addr),
    .din(ram0_din),
    .dout(ram0_dout)
);


hash_bucket x_hash_bucket1(
    .clk(clk),
    .rst_n(rst_n),
    .en(ram1_en),
    .wr(ram1_wr),
    .addr(ram1_addr),
    .din(ram1_din),
    .dout(ram1_dout)
);        

                        
endmodule



                        

                       





